
#include <vector>
#include <cassert>

/* need this or no forward declaration for glWindowPos2i */
//#define GL_GLEXT_PROTOTYPES

#include "common.h"
#include "SDL_mixer.h"

#include "DrumApp.h"
#include "TextureManager.h"
#include "SoundManager.h"
#include "ConfigManager.h"
#include "object.h"
#include "LogUtil.h"
#include "Composition.h"
#include "DefaultBitmapFont.h"
#include "Drumstick.h"
#include "Vector3D.h"

#ifdef _WIN32
#include "WGLBitmapFont.h"
#else
#include "GLXBitmapFont.h"
#endif

#define Cos(th) cos(3.1415926/180*(th))
#define Sin(th) sin(3.1415926/180*(th))

extern "C" { void glWindowPos2i(int x, int y); };

#ifdef WITH_SHADOWS
// for shadows, need EXT_framebuffer_object extension
// On Linux, this isn't really needed since we have a recent OpenGL 
// version there, but windows does not so we need to jump through hoops.
typedef void (APIENTRY *glGenFramebuffersEXT_Func)        (GLsizei n, GLuint *framebuffers);
typedef void (APIENTRY *glBindFramebufferEXT_Func)        (GLenum target, GLuint framebuffer);
typedef void (APIENTRY *glGenRenderbuffersEXT_Func)       (GLsizei n, GLuint *renderbuffers);
typedef void (APIENTRY *glBindRenderbufferEXT_Func)       (GLenum target, GLuint renderbuffer);
typedef void (APIENTRY *glRenderbufferStorageEXT_Func)    (GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
typedef void (APIENTRY *glFramebufferRenderbufferEXT_Func)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
typedef GLenum (APIENTRY *glCheckFramebufferStatusEXT_Func) (GLenum target);
typedef void (APIENTRY *glActiveTexture_Func)(GLenum texture);
typedef void (APIENTRY *glMultiTexCoord2f_Func)(GLenum target, GLfloat s, GLfloat t);

glGenFramebuffersEXT_Func         glGenFramebuffers = NULL;
glBindFramebufferEXT_Func         glBindFramebuffer = NULL;
glGenRenderbuffersEXT_Func        glGenRenderbuffers = NULL;
glBindRenderbufferEXT_Func        glBindRenderbuffer = NULL;
glRenderbufferStorageEXT_Func     glRenderbufferStorage = NULL;
glFramebufferRenderbufferEXT_Func glFramebufferRenderbuffer = NULL;
glCheckFramebufferStatusEXT_Func  glCheckFramebufferStatus = NULL;
glActiveTexture_Func              glActiveTextureA = NULL;
glMultiTexCoord2f_Func            glMultiTexCoord2fA = NULL;

bool ARB_multitexture = false;
bool EXT_framebuffer_object = false;
#endif

// blending 
static bool notify = false;
static double alpha = 1.0;


// XXX: move these into DrumApp.h or better yet, into
// concrete subclasses of the abstract Shape class that
// represent each object 

// mallet and pedal 
static int mangle = -45;
static int pangle = -25;
static bool m_inmotion = false;

// crash cymbal
static int cangle = 0; // between 0 and 360
static float cdecay = 1.0;
static bool c_inmotion = false;

// ride cymbal
static int rangle = 0; // between 0 and 360
static float rdecay = 2.0;
static bool r_inmotion = false;

// load composition flag
static int loaded = 0;

#ifdef WITH_SHADOWS
// shadows
bool         MultiTex;  //  Whether there are more than 1 texture units
                        //  Setting of false implies no shadow support
double       Svec[4];   //  Texture planes S
double       Tvec[4];   //  Texture planes T
double       Rvec[4];   //  Texture planes R
double       Qvec[4];   //  Texture planes Q
int          shadowdim; //  Size of shadow map textures
int          Sdim;      //  Texture width
int          Tdim;      //  Texture height
int          NoAmbient; //  Enable ambient shadows
unsigned int framebuf=0;//  Frame buffer id
#endif

__BEGIN_NAMESPACE_DECL_

using namespace std;

void 
DrumApp::ErrCheck(const char* where)
{
   int err = glGetError();
   if (err) 
   {
      ERROR << gluErrorString(err) << " " << where;
   }
}


/**
 * LFPrint() -- Function to interpret linefeeds and move the virtual
 *              cursor to a new row upon receiving a linefeed.
 *              Always starts at row 1 in the top left of the window.
 */
#define MAX_TEXT_SIZE 8192
void 
DrumApp::LFPrint (const char *format, ...)
{
    int row = 1;
    int fontHeight = 12;
    char buffer[MAX_TEXT_SIZE];
    char *str;
    va_list ap;

    va_start (ap, format);
    vsnprintf (buffer, MAX_TEXT_SIZE, format, ap);
    va_end (ap);

    // Set the starting cursor position
    glWindowPos2i (5, height_ - (row++ * fontHeight));

    // Tokenize the string by linefeeds
    str = strtok (buffer,"\n");
    while (str != NULL)
    {
        font->Print (str);

        // move the cursor to the next line
        glWindowPos2i (5, height_ - (row++ * fontHeight));

        str = strtok(NULL,"\n");
    }
    
    return;
}

void
DrumApp::init()
{
    font = NULL;
    fov = 55.0;
    aspect = 1.0;
    dim = 36.0;
    light0_angle = 0.0;
    drawAxes = false;
    displayHelp = false;
    displayFlag = false;
    lightsOn = true;
    shadowsOn = false;
    spotlightOn = false;
    spotlightCutoff = 45;
    lightRotateOn = false;
    cameraRotateOn = false;
    extraObjectsOn = false;
    fanSpeed = 0;
    mode = 2;
    polygonMode = 0;
    th_x = 30;
    th_y = 160;
    ambient = 20;
    diffuse = 70;
    specular = 60;
    shininess = 0;
    playbackState = DRUM_PLAYBACK_STOPPED;
    compositionNum = 0;
}

// Default Constructor
DrumApp::DrumApp ()
    : fan(100, 5)
{
    init();
}

// One-arg constructor
DrumApp::DrumApp (const char *title)
    : SDL_GLApp(title), fan(100, 5)
{
    init();
}

// Destructor 
DrumApp::~DrumApp()
{
    if (font != NULL)
        delete font;
}

void
DrumApp::InitScene()
{
    // Create a quadric to be used as the object showing where the light 
    // source is
    sphere = gluNewQuadric();
    assert (sphere != NULL);
    gluQuadricDrawStyle(sphere, GLU_FILL);
    gluQuadricOrientation(sphere, GLU_OUTSIDE);
    gluQuadricNormals(sphere, GLU_SMOOTH);
    
    // Add the fan; set scale and translation from origin
    fan.setOffset (0.0, 1.0, 0.0);
    fan.setScale (0.33, 0.33, 0.33);
    scene.push_back(&fan);
}

void
DrumApp::LoadConfiguration()
{
    // Get the configuration manager singleton instance
    ConfigManager& config = ConfigManager::getInstance();

#ifndef DEFAULT_X11_FONT
 #define DEFAULT_X11_FONT "-*-clean-*-*-*-*-*-*-*-*-*-*-iso8859-*"
#endif
#ifndef DEFAULT_WINDOWS_FONT
 #define DEFAULT_WINDOWS_FONT "Courier New"
#endif

    // Setup fonts
    bool useFontExtensions = 
        (strcmp(config.getProperty("UseFontExtensions", "false"), "true")==0);

    if (useFontExtensions)
    {
#ifdef _WIN32
        const char *fontName = 
            config.getProperty("NativeFont.Windows", DEFAULT_WINDOWS_FONT);
        font = new WGLBitmapFont();
        font->LoadFont(fontName);
#else
        const char *fontName = 
            config.getProperty("NativeFont.X11", DEFAULT_X11_FONT);
        font = new GLXBitmapFont();
        font->LoadFont(fontName);
#endif
    }
    else 
    {
        // Load our BitmapFont
        font = new DefaultBitmapFont();
    }

    // Initialize the playback state according to the config file.
    bool playing = 
        strcmp(config.getProperty("InitialStatePlaying", "false"), "true")==0;
    playbackState = playing ? (DRUM_PLAYBACK_PLAYING) : DRUM_PLAYBACK_PAUSED;

    // Set the initial view mode according to the config file
    const char *configMode = 
        config.getProperty("InitialViewMode","PerspectiveFPS");
    if (strcmp(configMode,"Ortho")==0)
        mode = 0;
    else if (strcmp(configMode,"PerspectiveFixed")==0)
        mode = 1;
    else if (strcmp(configMode,"PerspectiveFPS")==0)
        mode = 2;
    else if (strcmp(configMode,"MultiView")==0)
        mode = 3;
    else
        WARN << "Invalid InitialViewMode [" << configMode << "]";

    // Set the initial polygonMode
    const char *polyMode = config.getProperty("InitialPolygonMode","Fill");
    if (strcmp(polyMode,"Fill")==0)
        polygonMode = 0;
    else if (strcmp(polyMode,"Line")==0)
        polygonMode = 1;
    else if (strcmp(polyMode,"Point")==0)
        polygonMode = 2;
    else
        WARN << "Invalid InitialPolygonMode [" << polyMode << "]";

    // Set the initial field of view
    int configFOV = atoi(config.getProperty("InitialFieldOfView","55"));
    if (configFOV >= 20 && configFOV <= 120)
        fov = configFOV;
    else
        WARN << "InitialFieldOfView [" << polyMode << "] must be between 20 and 120";

    // Set whether the spotlight is on by default
    bool configSpotlightOn = 
        strcmp(config.getProperty("SpotlightOn", "false"), "true")==0;
    spotlightOn = configSpotlightOn;

    // Set the spotlight cutoff angle
    int configCutoff = atoi(config.getProperty("SpotlightCutoff","45"));
    if (configCutoff >= 0 && configCutoff <= 90)
        spotlightCutoff = configCutoff;
    else
        WARN << "SpotlightCutoff [" << polyMode << "] must be between 0 and 90";

    // Set whether the light is rotating by default
    bool configLightRotate = 
        strcmp(config.getProperty("LightRotateOn", "false"), "true")==0;
    lightRotateOn = configLightRotate;

    // Set whether the camera is rotating by default
    bool configCameraRotate = 
        strcmp(config.getProperty("CameraRotateOn", "false"), "true")==0;
    cameraRotateOn = configCameraRotate;

    // Turn shadows on according to config file
    shadowsOn = 
        strcmp(config.getProperty("ShadowsOn", "false"), "true")==0;

    // Turn on extra objects according to config file
    extraObjectsOn = 
        strcmp(config.getProperty("ExtraObjectsOn", "false"), "true")==0;

    // Enable textures according to config file
    TextureManager::getInstance().setDisabled( 
        strcmp(config.getProperty("TexturesDisabled", "false"), "true")==0
    );

    // Load the first composition defined in the config file
#define COMPOSITION_FILE "SampleComposition.txt"
    compositionNum = 1;
    if (!LoadComposition(compositionNum, COMPOSITION_FILE))
        compositionNum = 0;
}

void
DrumApp::InitApp()
{
    // Call superclass method first
    SDL_GLApp::InitApp();

    // Load configuration from config.cfg file
    LoadConfiguration();

    // Setup sounds
    SoundManager& soundManager = SoundManager::getInstance();
    DEBUG << "Loading sound files...";
    soundManager.Load ("cymbal.wav",      "Ride");
    soundManager.Load ("cymbalcrash.wav", "Crash");
    soundManager.Load ("hihat.wav",       "Hihat");
    soundManager.Load ("kickdrum.wav",    "Kick");
    soundManager.Load ("snaredrum.wav",   "Snare");
    soundManager.Load ("tom12.wav",       "Tom12");
    soundManager.Load ("tom13.wav",       "Tom13");
    soundManager.Load ("tom16.wav",       "Tom16");
    DEBUG << "Done loading sound files...";

#ifdef WITH_SHADOWS
    // Needed for Shadow Mapping
    glDepthFunc(GL_LEQUAL);
    glPolygonOffset(20,0);
#endif

    // Enable cull facing
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);  // remove back facing polygons
    glFrontFace(GL_CCW);

    // Nice perspective calculations?
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

    // Set polygon mode to GL_POINT, GL_LINE or GL_FILL (default)
    ApplyPolygonMode();

    // Apply the specular color after the texture
    glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);

    // Use two sided lighting
    //glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);

    // If mode is 2, turn off cursor
    if (mode == 2)
        SDL_ShowCursor(SDL_DISABLE);
    else
        SDL_ShowCursor(SDL_ENABLE);

    // Set the player position
    player.setPosition (0.0, 30.0, -60.0);
    player.setViewAngle (180.0);
    player.setPitch (20.0);

    // Load textures
    // TODO: Eventually move this logic into each object.
    unsigned int texture;

    TextureManager& texManager = TextureManager::getInstance();
    if (texManager.getTexture("concrete.jpg", texture, true))
    {
        room.setFloorTexture(RoomTexture(texture, 4.0, 4.0));
        room.setUseTexture(true);
    }

    if (texManager.getTexture("door.jpg", texture, true))
    {
        room.setBackWallTexture(RoomTexture(texture, 1.0, 1.0));
        room.setUseTexture(true);
    }
    if (texManager.getTexture("bricks.jpg", texture, true))
    {
        room.setLeftWallTexture(RoomTexture(texture, 1.0, 1.5));
        room.setRightWallTexture(RoomTexture(texture, 1.0, 1.5));
        room.setFrontWallTexture(RoomTexture(texture, 1.0, 1.5));
        room.setUseTexture(true);
    }
    if (texManager.getTexture("nightsky.jpg", texture, true))
    {
        room.setCeilingTexture(RoomTexture(texture, 1.0, 1.0));
        room.setUseTexture(true);
    }

    cymbalDispList = LoadOBJ((char *)"Cymbal16.obj");
    //cymbalDispList = LoadOBJ((char *)"Cymbal.obj");
    drumstickDispList = LoadOBJ((char *)"Drumstick.obj");
    leftDrumstick.setDisplayList(drumstickDispList);
    leftDrumstick.setOffset (0.0, 12.0, 0.0);
    rightDrumstick.setDisplayList(drumstickDispList);
    rightDrumstick.setOffset (0.0, 12.0, 0.0);

    // Each drumstick has a different position for each instrument
    // Define what those positions are here;
    leftDrumstick.addLocation("Snare",DrumstickLocation( 22.0, 12.0,-33.5,-10.0,-15.0, 25.0));
    leftDrumstick.addLocation("Hihat",DrumstickLocation( 15.0, 20.5,-37.5,  0.0, 30.0,  0.0));
    leftDrumstick.addLocation("Crash",DrumstickLocation( 21.0, 20.0,-32.5,-15.0,-20.0,  0.0));
    leftDrumstick.addLocation("Ride", DrumstickLocation( -5.0, 16.5,-21.0,-15.0,-45.0,  0.0));
    leftDrumstick.addLocation("Tom12",DrumstickLocation( 12.0, 21.5,-24.0,  0.0,-15.0,  0.0));
    leftDrumstick.addLocation("Tom13",DrumstickLocation(  0.0, 23.0,-24.0,  0.0,-20.0,  0.0));
    leftDrumstick.addLocation("Tom16",DrumstickLocation( -1.0, 13.0,-27.0,  0.0,-50.0,  0.0));

    rightDrumstick.addLocation("Snare",DrumstickLocation(  4.0,  9.5,-30.0,-10.0, 30.0,  0.0));
    rightDrumstick.addLocation("Hihat",DrumstickLocation(  5.0, 20.5,-27.0,  0.0, 70.0,  0.0));
    rightDrumstick.addLocation("Crash",DrumstickLocation(  0.0, 21.0,-31.0,-15.0, 30.0,  0.0));
    rightDrumstick.addLocation("Ride", DrumstickLocation(-12.0, 17.0,-26.0,-15.0,-30.0,  0.0));
    rightDrumstick.addLocation("Tom12",DrumstickLocation(  0.0, 21.5,-24.0,  0.0, 10.0,  0.0));
    rightDrumstick.addLocation("Tom13",DrumstickLocation(-12.0, 23.0,-25.0,  0.0, 10.0,  0.0));
    rightDrumstick.addLocation("Tom16",DrumstickLocation( -4.0, 13.5,-30.0,  0.0,-50.0,  0.0));

    // Set initial locations
    leftDrumstick.setLocation("Snare");
    rightDrumstick.setLocation("Hihat");
    
    // Load display lists for all the models
    bassdrum = LoadOBJ((char *)"BassDrum.obj");
    snare = LoadOBJ((char *)"Snare.obj");
    tom12 = LoadOBJ((char *)"Tom12.obj");
    tom13 = LoadOBJ((char *)"Tom13.obj");
    tom16 = LoadOBJ((char *)"Tom16.obj");
    boomCrash = LoadOBJ((char *)"BoomCymbalStand.obj");
    kickPedal = LoadOBJ((char *)"KickPedal.obj");
    mallet = LoadOBJ((char *)"Mallet.obj");
    pedal = LoadOBJ((char *)"Pedal.obj");
    hihat14 = LoadOBJ((char *)"Hihat14.obj");
    hihatStand = LoadOBJ((char *)"HiHatStand.obj");
    ride20 = LoadOBJ((char *)"Ride20.obj");
    rideStand = LoadOBJ((char *)"RideStand.obj");
    microphone = LoadOBJ((char *)"Microphone.obj");
    guitar = LoadOBJ((char *)"Guitar.obj");

    // Init shadow map; null op if compiled w/o WITH_SHADOWS
    InitMap();

    InitScene();
}

void
DrumApp::DisplayHelp()
{
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3d (0.0, 0.8, 0.8);
    LFPrint(" Orientation/Viewing Angle/Zoom/Projection:\n"
            "  Left/Right- Rotate the image counter-clockwise/clockwise around the Y-axis\n"
            "  Up/Down   - Rotate the image around the X-axis\n"
            "  Home      - Set the orientation around X/Y axes to zero degrees\n"
            "  PgUp/PgDn - Zoom in/out by decreasing/increasing dimension of world\n"
            "  +/-       - Zoom in/out by decreasing/increasing field of view angle\n"
            " \n "
            " General:\n"
            "  Esc       - Exit the application.\n"
            "  Spacebar  - Toggle playback (play/pause)\n"
            "  Tab       - Toggle shadows (shadow map)\n"
            "  []        - Decrease/Increase the number of subdivisions for walls\n"
            "  /         - Toggle spotlight mode on/off\n"
            "  ,.        - Decrease/Increase the cutoff angle in spotlight mode\n"
            "  e         - Toggle display of global state variables\n"
            "  h         - Toggle display of this help screen\n"
            "  m         - Toggle between different modes\n"
            "              0 = Orthogonal projection\n"
            "              1 = Perspective projection\n"
            "              2 = Perspective projection w/ fps style camera\n"
            "              3 = Multiple viewport w/ fixed perspective\n"
            "  n         - Advance to next composition\n"
            "  p         - Change the polygon mode between point, line and fill\n"
            "  r         - Toggle light rotation on/off\n"
            "  x         - Toggle display of x, y and z axes\n"
            " \n "
            " FPS Mode:\n"
            "  w/s       - Move the player forward/backward\n"
            "  a/d       - Make the player look to the left/right\n"
            "  z         - Reset player position\n"
            "  Mouse     - Change viewing angle and pitch (up/down)\n"
            " \n "
            " Drum Set:\n"
            "  3         - Hit the Crash cymbal\n"
            "  4         - Hit the Snare drum\n"
            "  5         - Hit the Hi-hats\n"
            "  6         - Hit the Kick Drum\n"
            "  7         - Hit the Ride Cymbal\n"
            "  8         - Hit the 12\" Tom\n"
            "  9         - Hit the 13\" Tom\n"
            "  0         - Hit the 16\" Tom\n"
            " \n "
            " Lighting:\n"
            "  1         - Toggle all lighting on/off\n"
            "  u/U       - Turn up/down ambient component of rotating light\n"
            "  i/I       - Turn up/down diffuse component of rotating light\n"
            "  o/O       - Turn up/down specular component of rotating light"
           );
}

void
DrumApp::DisplayNotification()
{
    GLuint texid;
    //int borderx = width_/8;
    //int bordery = height_/8;
    int borderx = 320/2;
    int bordery = 240/2;
    int centerx = width_/2-1;
    int centery = height_/2-1;

    glPushAttrib(GL_ENABLE_BIT);
    glDisable(GL_LIGHTING);
    glEnable(GL_BLEND);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_TEXTURE_2D);
    glPushMatrix();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glViewport (0, 0, width_, height_);
    glOrtho (0, width_, 0, height_, -0.1, 0.1);
    DEBUG << "alpha=" << alpha;
    if (playbackState == DRUM_PLAYBACK_PLAYING)
    {
        if (TextureManager::getInstance().getTexture("play.png", texid, true))
            glBindTexture (GL_TEXTURE_2D, texid);
        else
            ERROR << "can't load texture";
    } 
    else if (playbackState == DRUM_PLAYBACK_PAUSED)
    {
        if (TextureManager::getInstance().getTexture("pause.png", texid, true))
            glBindTexture (GL_TEXTURE_2D, texid);
        else
            ERROR << "can't load texture";
    }
    glColor4d (1.0, 1.0, 1.0, alpha);
    glBegin(GL_QUADS);
      glTexCoord2d (0.0, 0.0); glVertex2d (centerx-borderx, centery-bordery);
      glTexCoord2d (1.0, 0.0); glVertex2d (centerx+borderx, centery-bordery);
      glTexCoord2d (1.0, 1.0); glVertex2d (centerx+borderx, centery+bordery);
      glTexCoord2d (0.0, 1.0); glVertex2d (centerx-borderx, centery+bordery);
    glEnd();
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    glPopAttrib();
}

void
DrumApp::Process()
{
    static Uint32 last;
    static bool first = true;
    static double time = 0.0;
    static double duration = 0.0;

    Uint32 now, elapsed;
    Uint8 *keystate = SDL_GetKeyState(NULL);

    if (first)
    {
        // On first call, initialize the number of milliseconds 
        // corresponding to the last call to Process()
        last = SDL_GetTicks();
        first = false;
    }
    else if (playbackState == DRUM_PLAYBACK_PAUSED)
    {
        // when we are paused, we just update the last variable so that we 
        // don't miscalculate the elapsed time when we start playing again.
        last = SDL_GetTicks();
    }
    else if (playbackState == DRUM_PLAYBACK_PLAYING)
    {
        Note note;

        // Get current number of milliseconds
        now = SDL_GetTicks();
        // Compute elapsed milliseconds since last call to Process()
        elapsed = now - last;
        // Set last to now
        last = now;
        
        // Modify our musical time tracking variable
        time += (double)elapsed/1000;
        
        composition.getCurrentNote(note);
        DEBUG << "time=" << time << ", elapsed=" << elapsed << ", duration=" << duration;

        if (composition.isPlaying())
        {
            if ((time > duration) || ((duration - time) <= 0.01))
            {
                // update the time by mod'ing it with the duration of the
                // current note
                if (time > duration && duration >= 0.001)
                    time = fmod(time, duration);
                else if ((duration - time) <= 0.01)
                    time = duration - time;
         
                // Play the instruments
                if ((note.instruments & NOTE_HIHAT) == NOTE_HIHAT)
                    PlayInstrument("Hihat", DRUMSTICK_POLICY_PREFER_RIGHT);
                if ((note.instruments & NOTE_SNARE) == NOTE_SNARE)
                    PlayInstrument("Snare", DRUMSTICK_POLICY_PREFER_LEFT);
                if ((note.instruments & NOTE_KICK) == NOTE_KICK)
                {
                    SoundManager::getInstance().Play("Kick");
                    m_inmotion = true;
                }
                if ((note.instruments & NOTE_CRASH) == NOTE_CRASH)
                {
                    PlayInstrument("Crash", DRUMSTICK_POLICY_PREFER_LEFT);
                    c_inmotion = true;
                    cangle = 0;
                    cdecay = 1.0;
                }
                if ((note.instruments & NOTE_RIDE) == NOTE_RIDE)
                {
                    PlayInstrument("Ride", DRUMSTICK_POLICY_PREFER_RIGHT);
                    r_inmotion = true;
                    rangle = 0;
                    rdecay = 1.0;
                }
                if ((note.instruments & NOTE_TOM12) == NOTE_TOM12)
                    PlayInstrument("Tom12", DRUMSTICK_POLICY_PREFER_RIGHT);
                if ((note.instruments & NOTE_TOM13) == NOTE_TOM13)
                    PlayInstrument("Tom13", DRUMSTICK_POLICY_PREFER_RIGHT);
                if ((note.instruments & NOTE_TOM16) == NOTE_TOM16)
                    PlayInstrument("Tom16", DRUMSTICK_POLICY_PREFER_RIGHT);

                // Advance to the next note
                duration = note.duration;
                composition.next();
            }    
        }
    }

    // state machine for left drumstick
    if (leftDrumstick.isInMotion())
        leftDrumstick.advanceState();

    // state machine for right drumstick
    if (rightDrumstick.isInMotion())
        rightDrumstick.advanceState();

    // state machine for notification
    if (notify)
    {
        if (alpha > 0.001) { alpha -= 0.01; }
        else { notify = false; alpha = 1.0; }
    }

    // state machine for composition loading notification
    if (loaded)
    {
        loaded++;
        if (loaded == 150) 
            loaded = 0;
    }

    // state machine for kick drum mallet and pedal
    if (m_inmotion && mangle == -45) { mangle = -30; pangle = -20; }
    else if (mangle == -30) { mangle = -15; pangle = -15; }
    else if (mangle == -15) { mangle = 0;   pangle = -10; }
    else if (mangle == 0)   { mangle = -14; pangle = -14; }
    else if (mangle == -14) { mangle = -29; pangle = -19; }
    else if (mangle == -29) { mangle = -45; pangle = -25; m_inmotion = false; }

    // state machine for crash cymbal
    if (c_inmotion && cdecay >= 0.01) 
    { 
        cangle = (cangle + 20) % 360; 
        cdecay -= 0.02;
    }
    else if (c_inmotion && cdecay < 0.01)
    { 
        cangle = 0;
        cdecay = 1.0;
        c_inmotion = false;
        DEBUG << "Done with cymbal motion";
    }

    // state machine for ride cymbal
    if (r_inmotion && rdecay >= 0.01) 
    { 
        rangle = (rangle + 20) % 360; 
        rdecay -= 0.02;
    }
    else if (r_inmotion && rdecay < 0.01)
    { 
        rangle = 0;
        rdecay = 1.0;
        r_inmotion = false;
        DEBUG << "Done with cymbal motion";
    }

#define DEGREE_INC 3
    // Adjust angle of rotation
    if (mode == 0 || mode == 1)
    {
        if (cameraRotateOn)
            th_y -= 1;
        else if (keystate[SDLK_LEFT])
            th_y -= DEGREE_INC;
        else if (keystate[SDLK_RIGHT])
            th_y += DEGREE_INC;
        if (keystate[SDLK_UP])
            th_x -= DEGREE_INC;
        else if (keystate[SDLK_DOWN])
            th_x += DEGREE_INC;

        if (keystate[SDLK_HOME])
            th_x = th_y = 0;
    }

    // first person shooter camera controls 
    else if (mode == 2)
    {
        if (keystate[SDLK_w])
            player.moveForward();
        else if (keystate[SDLK_s])
            player.moveBackward();

        if (keystate[SDLK_a])
            player.lookLeft(50.0);
        else if (keystate[SDLK_d])
            player.lookRight(50.0);

        if (keystate[SDLK_z])
        {
            player.reset();
            player.setPosition(0.0, 0.5, 1.0);
        }
    }

    // Valid for modes 1 & 2
    // adjust field of view */
    if ((SDL_GetModState() & KMOD_SHIFT) && 
         keystate[SDLK_EQUALS])
    {
        fov -= 1.0;
        Projection();
    }
    else if (keystate[SDLK_MINUS])
    {
        fov += 1.0;
        Projection();
    }

    /* adjust dimension (size of clipping cube) */
    if (keystate[SDLK_PAGEUP])
    {
        dim -= 0.1;
        Projection();
    }
    else if (keystate[SDLK_PAGEDOWN])
    {
        dim += 0.1;
        Projection();
    }

    /* Change number of subdivisions in room */
    if (keystate[SDLK_LEFTBRACKET])
    {
        int subdiv = room.getSubdivisions();
        subdiv = (subdiv > 0) ? subdiv-1 : 0;
        room.setSubdivisions(subdiv);
    }
    else if (keystate[SDLK_RIGHTBRACKET])
    {
        int subdiv = room.getSubdivisions();
        subdiv = (subdiv < 250) ? subdiv+1 : 250;
        room.setSubdivisions(subdiv);
    }

    /* modify lighting parameters */
    if (keystate[SDLK_u])
        ambient  += (SDL_GetModState() & KMOD_SHIFT) ? 1 : -1;
    else if (keystate[SDLK_i])
        diffuse  += (SDL_GetModState() & KMOD_SHIFT) ? 1 : -1;
    else if (keystate[SDLK_o])
        specular += (SDL_GetModState() & KMOD_SHIFT) ? 1 : -1;
    else if (keystate[SDLK_p])
        shininess += (SDL_GetModState() & KMOD_SHIFT) ? 1 : -1;

    /* clamp ambient, diffuse and specular lighting to values from 0 to 100 */
    ambient  = (ambient > 100)  ? 100 : (ambient < 0)  ? 0 : ambient;
    diffuse  = (diffuse > 100)  ? 100 : (diffuse < 0)  ? 0 : diffuse;
    specular = (specular > 100) ? 100 : (specular < 0) ? 0 : specular;

    // Get elapsed time in ticks (milliseconds) since application start
    double t = SDL_GetTicks()/1000.0;

    // Process() is called every 20ms, or 50 times a second.
    // For simplicity, we'll define the light to rotate at 50 degrees/second
    // so just add one to the angle
    if (lightRotateOn)
        light0_angle = (int)fmod(light0_angle+1, 360);
    
    if (fanSpeed > 0)
        fan.setAngleOfRotation((int)fmod(90*fanSpeed*t, 360));

    // Recalculate shadow map; null op if compiled w/o WITH_SHADOWS
    if (shadowsOn && mode >= 0 && mode <= 2)
        ShadowMap();

    RequestRedisplay();
}

void
DrumApp::Display()
{

    /* set the background color to black and clear the buffer */
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);

    /* Enable Z-buffering and clear the depth buffer */
    glEnable(GL_DEPTH_TEST);
    glClear(GL_DEPTH_BUFFER_BIT);

    /* Load identity matrix */
    glLoadIdentity();

    /* set foreground color to yellow  */
    glColor3d(1.0, 1.0, 0.0);

    /* Perform rotation transformations */
    if (mode == 0)
    {
        //  Orthogonal - set world orientation
        glRotatef (th_x, 1.0, 0.0, 0.0);
        glRotatef (th_y, 0.0, 1.0, 0.0);
    }
    else if (mode == 1)
    {
        // Perspective - set eye position

        /**
         * Normally if we are converting from spherical coordinates, the 
         * equations come out as follows, where phi is the angle of 
         * rotation from the X-Z plane to the Y-axis, and theta is the 
         * angle of rotation around the Y-axis, measured from the X-axis.
         * Ex = r * Cos(phi) * Cos(theta)
         * Ey = r            * Sin(phi)
         * Ez = r * Cos(phi) * Sin(theta)
         * However, since we want the eye to be looking down the Z-axis
         * instead of the X-axis (to correspond with the Orthogonal projection
         * in OpenGL), we subtract 90 degrees to theta in the above
         * equations.  Given that Cos(theta-90) = -Sin(theta), and 
         * Sin(theta-90) = Cos(theta) we can change the equations to include 
         * this rotation.
         * In the following phi = th_x, and theta = th_y
         */
        double Ex = -2*dim*Sin(th_y)*Cos(th_x);
        double Ey = +2*dim          *Sin(th_x);
        double Ez = +2*dim*Cos(th_y)*Cos(th_x);
        gluLookAt(Ex,Ey,Ez , 0,0,0 , 0,Cos(th_x),0);
    }
    else if (mode == 2)
    {
        // Put the pointer back in the middle of the window
        SDL_WarpMouse(width_/2,height_/2);

        /**
         * The following code is adapted from lesson10 of NeHe's tutorial: 
         * http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=10
         * The idea is that instead of rotating the camera itself, we
         * rotate the world around the camera by translating the world
         * by the negative camera position in the world, and then rotating
         * by 360 minus the viewing angle.
         */
        double Ex, Ey, Ez;
        double angle = 360 - player.getViewAngle();
        double pitch = player.getPitch();

        player.getPosition(Ex, Ey, Ez);

        glRotated (pitch, 1.0, 0.0, 0.0);
        glRotated (angle, 0.0, 1.0, 0.0);
        glTranslated (-Ex, -Ey, -Ez);
    }
    else if (mode == 3)
    {
        int halfwidth = (width_/2)+1;
        int halfheight = (height_/2)+1;

        double Ex, Ey, Ez, angle, pitch;

        Player cam1;
        cam1.setPosition (0.0, 33.3, -60.0);
        cam1.setViewAngle (180.0);
        cam1.setPitch (20.0);
        cam1.getPosition (Ex, Ey, Ez);
        angle = 360 - cam1.getViewAngle();
        pitch = cam1.getPitch();

        glEnable(GL_SCISSOR_TEST);

        // bottom left, side view 
        glViewport (0, 0, halfwidth, halfheight);
        glScissor (0, 0, halfwidth, halfheight);
        glMatrixMode (GL_PROJECTION);
        glLoadIdentity();
        gluPerspective (fov, aspect, dim/10.0, 10.0*dim);
        glMatrixMode (GL_MODELVIEW);
        glLoadIdentity();
        glRotated (pitch, 1.0, 0.0, 0.0);
        glRotated (angle, 0.0, 1.0, 0.0);
        glTranslated (-Ex, -Ey, -Ez);
        glRotated (90, 0.0, 1.0, 0.0);
        DrawScene(1);

        // bottom right, front view
        glViewport (halfwidth, 0, halfwidth, halfheight);
        glScissor (halfwidth, 0, halfwidth, halfheight);
        glMatrixMode (GL_PROJECTION);
        glLoadIdentity();
        gluPerspective (fov, aspect, dim/10.0, 10.0*dim);
        glMatrixMode (GL_MODELVIEW);
        glLoadIdentity();
        glRotated (pitch, 1.0, 0.0, 0.0);
        glRotated (angle, 0.0, 1.0, 0.0);
        glTranslated (-Ex, -Ey, -Ez);
        DrawScene(1);

        // top left, back view
        glViewport (0, halfheight, halfwidth, halfheight);
        glScissor (0, halfheight, halfwidth, halfheight);
        glMatrixMode (GL_PROJECTION);
        glLoadIdentity();
        gluPerspective (fov, aspect, dim/10.0, 10.0*dim);
        glMatrixMode (GL_MODELVIEW);
        glLoadIdentity();
        glRotated (pitch, 1.0, 0.0, 0.0);
        glRotated (angle, 0.0, 1.0, 0.0);
        glTranslated (-Ex, -Ey, -Ez);
        glRotated (180, 0.0, 1.0, 0.0);
        DrawScene(1);

        // top right, top view
        glViewport (halfwidth, halfheight, halfwidth, halfheight);
        glScissor (halfwidth, halfheight, halfwidth, halfheight);
        glMatrixMode (GL_PROJECTION);
        glLoadIdentity();
        gluPerspective (fov, aspect, dim/10.0, 10.0*dim);
        glMatrixMode (GL_MODELVIEW);
        glLoadIdentity();
        Ex = -2*dim*Sin(180)*Cos(72);
        Ey = +2*dim         *Sin(72);
        Ez = +2*dim*Cos(180)*Cos(72);
        gluLookAt(Ex,Ey,Ez , 0,0,0 , 0,1,0);
    }

    // Draw all shapes in our scene vector.
    //vector<Shape*>::iterator it;
    //for (it = scene.begin(); it != scene.end(); ++it)
    //    (*it)->draw();
    
    //  Shadow pass - needed if ambient shadows are not supported
#ifdef WITH_SHADOWS
    if (shadowsOn && mode >= 0 && mode <= 2)
    {
        if (NoAmbient)
        {
            //  Draw scene with dim lighting (including walls)
            DrawScene(-1);
            // Enable alpha test so that shadowed fragments are discarded
            glAlphaFunc(GL_GREATER,0.9);
            glEnable(GL_ALPHA_TEST);
        }

        // Set up shadow texture comparison
        glActiveTextureA(GL_TEXTURE1);
        glEnable(GL_TEXTURE_2D);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_MODE,GL_COMPARE_R_TO_TEXTURE);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_FUNC,GL_LEQUAL);

        // Set up the eye plane for projecting the shadow map on the scene
        glEnable(GL_TEXTURE_GEN_S); glTexGendv(GL_S,GL_EYE_PLANE,Svec);
        glEnable(GL_TEXTURE_GEN_T); glTexGendv(GL_T,GL_EYE_PLANE,Tvec);
        glEnable(GL_TEXTURE_GEN_R); glTexGendv(GL_R,GL_EYE_PLANE,Rvec);
        glEnable(GL_TEXTURE_GEN_Q); glTexGendv(GL_Q,GL_EYE_PLANE,Qvec);
        glActiveTextureA(GL_TEXTURE0);
    }
#endif

    ErrCheck((char *)"display2");

    // Draw the scene with lights on
    DrawScene(1);

#ifdef WITH_SHADOWS
    if (shadowsOn && mode >= 0 && mode <= 2)
    {
        //  Disable textures and texture generation
        glActiveTextureA(GL_TEXTURE1);
        glDisable(GL_TEXTURE_2D);
        glDisable(GL_TEXTURE_GEN_S);
        glDisable(GL_TEXTURE_GEN_T);
        glDisable(GL_TEXTURE_GEN_R);
        glDisable(GL_TEXTURE_GEN_Q);
        glActiveTextureA(GL_TEXTURE0);

        //  Disable alpha test if the shadow pass is done
        if (NoAmbient) glDisable(GL_ALPHA_TEST);
        ErrCheck("display - image");
    }
#endif

    if (mode == 3)
        glDisable (GL_SCISSOR_TEST);

    /* Display help if flag is set */
    if (displayHelp)
        DisplayHelp();

    /* Display Notification if requested */
    if (notify)
        DisplayNotification();

    /* Display notification when composition loaded */
    if (loaded)
    {
        glColor3d (1.0, 1.0, 1.0);
        glWindowPos2i (5,height_/2);
        font->Print ("Loaded composition \"%s\"", composition.getTitle());
    }

    /* Write state information at the bottom of the window */
    if (displayFlag)
    {
        glColor3d (1.0, 1.0, 1.0);
        // Display FPS at top left
        glWindowPos2i (5,height_-20);
        font->Print ("FPS=%.2f", fps_);
        glWindowPos2i (5,65);
        font->Print ("Subdiv=%u Spot=%d Cutoff=%d", room.getSubdivisions(), 
                     spotlightOn, spotlightCutoff);
        glWindowPos2i (5,50);
        font->Print ("Ambient=%u Diffuse=%u Specular=%u", ambient,diffuse,specular);
        glWindowPos2i (5,35);
        font->Print ("Mode=%s", (mode==0) ? "Orthogonal" : 
                                (mode==1) ? "Projection" : 
                                (mode==2) ? "Projection(FPS)" 
                                          : "Projection(4-View)");
        glWindowPos2i (5,20);
        double x,y,z;
        player.getPosition(x,y,z);
        double angle = player.getViewAngle();
        double pitch = player.getPitch();
        font->Print ("Player(%.2f,%.2f,%.2f) Angle(%.2f) Pitch(%.2f)", 
                     x,y,z,angle,pitch);
        glWindowPos2i (5,5);
        font->Print ("th_x=%d th_y=%d dim=%.4f fov=%.4f fanspd=%d, AoR=%d", 
                     th_x, th_y, dim, fov, fanSpeed, fan.getAngleOfRotation());

#ifdef WITH_SHADOWS
        // Draw the shadow map
        if (shadowsOn && mode >= 0 && mode <= 2)
        {
            glPushAttrib(GL_ENABLE_BIT|GL_VIEWPORT_BIT);
            glPushMatrix();
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            glMatrixMode(GL_PROJECTION);
            glPushMatrix();
            glLoadIdentity();
            //  Aspect ratio of texture
            double rat=(double)Sdim/Tdim;
            int texwidth, texheight;
            if (rat > aspect)
            {
                texwidth = width_/4;
                texheight = texwidth*(1/rat);
            }
            else
            {
                texheight = height_/4;
                texwidth = texheight*rat;
            }
            //  Texture containing shadow map
            glActiveTextureA(GL_TEXTURE1);
            glViewport (0, 0, width_, height_);
            glOrtho (0, width_, 0, height_, -0.1, 0.1);
            //  Disable any manipulation of textures
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_MODE,GL_NONE);
            //  Display texture by drawing quad
            glColor3f(1,1,1);
            glEnable(GL_TEXTURE_2D);
            glDisable(GL_LIGHTING);
            glBegin(GL_QUADS);
              glMultiTexCoord2fA(GL_TEXTURE1,0,0);
              glVertex2f(width_-texwidth-5, 5);
              glMultiTexCoord2fA(GL_TEXTURE1,1,0);
              glVertex2f(width_-5, 5);
              glMultiTexCoord2fA(GL_TEXTURE1,1,1);
              glVertex2f(width_-5, texheight-5);
              glMultiTexCoord2fA(GL_TEXTURE1,0,1);
              glVertex2f(width_-texwidth-5, texheight-5);
            glEnd();
            glDisable(GL_TEXTURE_2D);
            //  Switch back to default texture
            glActiveTextureA(GL_TEXTURE0);
            glMatrixMode(GL_PROJECTION);
            glPopMatrix();
            glMatrixMode(GL_MODELVIEW);
            glPopMatrix();
            glPopAttrib();
        }
#endif // WITH_SHADOWS

    }


    /* Check for any errors */
    ErrCheck((char *)"display");

    /* flush the commands to server and swap the buffers */
    //glFlush();
    frameCount_++;
    SDL_GL_SwapBuffers();

    return;
}

void
DrumApp::Resize (int width, int height)
{
    // Call superclass method as we have to reset the video
    // mode based on the new width/height
    // XXX: In windows, our entire GL context is destroyed and
    // we must re-create it.  This is terrible news.  This needs
    // fixed in the future.
#ifndef _WIN32
    SDL_GLApp::Resize(width, height);
#endif

    /* calculate the aspect ratio. */
    aspect = (height_ > 0) ? (double)width_/height_: dim;

    /* Set the viewport to the full window size */
    glViewport (0, 0, width_, height_);

    /* setup the projection matrix according to new aspect ratio */
    Projection();

    RequestRedisplay();

    return;
}

void
DrumApp::Keyboard (const SDLKey& key)
{
    // toggle between perspective and orthogonal projections 
    if (key == SDLK_m)
    {
        mode = (mode+1) % 4;
        if (mode == 2)
        {
            SDL_ShowCursor(SDL_DISABLE);
        }
        else 
        {
            SDL_ShowCursor(SDL_ENABLE);
        }

        glViewport (0, 0, width_, height_);
        Projection();
    }

    // toggle pause/play
    else if (key == SDLK_SPACE)
    {
        notify = true; alpha = 1.0;
        if (playbackState == DRUM_PLAYBACK_PAUSED)
            playbackState = DRUM_PLAYBACK_PLAYING;
        else
            playbackState = DRUM_PLAYBACK_PAUSED;
    }

    // Toggle shadows
    else if (key == SDLK_TAB)
    {
#ifdef WITH_SHADOWS
        if (ARB_multitexture && EXT_framebuffer_object && MultiTex)
        {
            shadowsOn = !shadowsOn;
            DEBUG << "Shadows " << ((shadowsOn) ? "enabled" : "disabled");
        }
        else
        {
            WARN << "Multitexture or framebruffer object extensions not supported"
                 << ", disabling shadows";
            shadowsOn = false;
        }
#else
        WARN << "Shadow support not compiled in.";
#endif
    }

    // Toggle spotlight
    else if (key == SDLK_SLASH)
        spotlightOn = !spotlightOn;
    else if (key == SDLK_COMMA)
        spotlightCutoff = (spotlightCutoff<90) ? spotlightCutoff+5 : 90;
    else if (key == SDLK_PERIOD)
        spotlightCutoff = (spotlightCutoff>0) ? spotlightCutoff-5 : 0;

    // polygon mode (fill, line, point)
    else if (key == SDLK_p)
    {
        polygonMode = (polygonMode+1)%3;
        ApplyPolygonMode();
    }

    else if (key == SDLK_r)
        lightRotateOn = !lightRotateOn;

    else if (key == SDLK_c)
        cameraRotateOn = !cameraRotateOn;
    
    /* advance compositions */
    else if (key == SDLK_n)
        AdvanceComposition();

    /* toggle display of state variables */
    else if (key == SDLK_e)
        displayFlag = !displayFlag;

    /* toggle display of axes */
    else if (key == SDLK_x)
        drawAxes = !drawAxes;

    /* toggle fan speed */
    else if (key == SDLK_f)
        fanSpeed = (fanSpeed+1) % 4;

    /* toggle the lamp's light */
    else if (key == SDLK_1)
        lightsOn = !lightsOn;
    else if (key == SDLK_2)
        // TODO: toggle state of hi-hat?
        ;
    else if (key == SDLK_3)
    {
        PlayInstrument("Crash", DRUMSTICK_POLICY_PREFER_RIGHT);
        c_inmotion = true;
        cangle = 0;
        cdecay = 1.0;
    }
    else if (key == SDLK_4)
        PlayInstrument("Snare", DRUMSTICK_POLICY_PREFER_LEFT);
    else if (key == SDLK_5)
        PlayInstrument("Hihat", DRUMSTICK_POLICY_PREFER_RIGHT);
    else if (key == SDLK_6)
    {
        SoundManager::getInstance().Play("Kick");
        m_inmotion = true;
    }
    else if (key == SDLK_7)
    {
        PlayInstrument("Ride", DRUMSTICK_POLICY_PREFER_RIGHT);
        r_inmotion = true;
        rangle = 0;
        rdecay = 1.0;
    }
    else if (key == SDLK_8)
        PlayInstrument("Tom12", DRUMSTICK_POLICY_PREFER_RIGHT);
    else if (key == SDLK_9)
        PlayInstrument("Tom13", DRUMSTICK_POLICY_PREFER_RIGHT);
    else if (key == SDLK_0)
        PlayInstrument("Tom16", DRUMSTICK_POLICY_PREFER_RIGHT);

    /* Display help */
    else if (key == SDLK_h)
        displayHelp = !displayHelp;

    /* exit application */
    else if (key == SDLK_ESCAPE)
        done_ = true;

    /* clamp shininess to values from 0 to 7 */
    shininess = (shininess > 7) ? 7 : (shininess < 0) ? 0 : shininess;
        
    /* Ask to render frame again ASAP */
    RequestRedisplay();

    return;
}

void 
DrumApp::MouseMotion(Uint8 state, Uint16 x, Uint16 y)
{
    if (mode == 2)
    {
        // Calculate a differential from our last position
        int diff_x = x - width_/2;
        int diff_y = y - height_/2;

        if (diff_x != 0 || diff_y != 0)
        {
            // Use the differential to change the player's orientation
            player.lookRight(diff_x);
            player.lookUp(diff_y);
            RequestRedisplay();
        }
    }
}

void
DrumApp::Projection()
{
    /* change to the projection matrix and load identity matrix */
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity();

    /* glOrtho */
    /* Orthogonal perspective */
    if (mode)
        gluPerspective (fov, aspect, dim/10.0, 10.0*dim);
    else
        glOrtho (-aspect*dim, aspect*dim, -dim, dim, -dim, dim);

    /* change back to modelview matrix and load identity matrix */
    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity();
}

void
DrumApp::DrawAxes(double scale)
{
    glPushMatrix();
      glScaled (scale, scale, scale);
      glPushAttrib(GL_LIGHTING_BIT);
      glDisable(GL_LIGHTING);
      glBegin(GL_LINES);
        glVertex3d(0.0, 0.0, 0.0);
        glVertex3d(1.0, 0.0, 0.0);
        glVertex3d(0.0, 0.0, 0.0);
        glVertex3d(0.0, 1.0, 0.0);
        glVertex3d(0.0, 0.0, 0.0);
        glVertex3d(0.0, 0.0, 1.0);
      glEnd();

      /* Label the axes */
      glRasterPos3f (1.0, 0.0, 0.0);
      font->Print("X");
      glRasterPos3f (0.0, 1.0, 0.0);
      font->Print("Y");
      glRasterPos3f (0.0, 0.0, 1.0);
      font->Print("Z");
      glPopAttrib();
    glPopMatrix();
}

void
DrumApp::DrawScene(int light)
{
    Light(light);

    /* draw a 3d axis, if enabled */
    if (drawAxes)
        DrawAxes(12);

    // Don't draw the room since it doesn't cast a shadow 
    if (light)
    {
        glEnable(GL_NORMALIZE);
        glPushMatrix();
          glTranslated (0.0, 33.0, 0.0);
          glScaled (150.0, 90.0, 150.0);
          room.draw();
        glPopMatrix();
        glDisable(GL_NORMALIZE);
    }

    // Extra objects
    if (extraObjectsOn)
    {
      glPushMatrix();
        glTranslated (12.0, 5.5, 9.0);
        glPushMatrix();
          glRotated (-13, 0.0, 0.0, 1.0);
          glPushMatrix();
            glRotated (75, 0.0, 1.0, 0.0);
            glPushMatrix();
              glRotated (-40, 1.0, 0.0, 0.0);
              glCallList(guitar);
            glPopMatrix();
          glPopMatrix();
        glPopMatrix();
      glPopMatrix();
      glPushMatrix();
        glTranslated (-15.0, 15.335, 20.0);
        glRotated (-45, 0.0, 1.0, 0.0);
        glCallList(microphone);
      glPopMatrix();
    }

    // Drum set
      glPushMatrix();
        //glRotated (90, 1.0, 0.0, 0.0);
        glRotated (180, 0.0, 1.0, 0.0);
        glCallList(bassdrum);
      glPopMatrix();
      glPushMatrix();
        glTranslated (16.0, 10.0, -14.0);
        glRotated (15, 0.0, 0.0, 1.0);
        glRotated (-15, 1.0, 0.0, 0.0);
        glCallList(snare);
      glPopMatrix();
      glPushMatrix();
        glTranslated (6.543, 18.607, -1.215);
        glRotated (-55, 1.0, 0.0, 0.0);
        glCallList(tom12);
      glPopMatrix();
      glPushMatrix();
        glTranslated (-7.893, 19.0, -2.850);
        glRotated (-55, 1.0, 0.0, 0.0);
        glCallList(tom13);
      glPopMatrix();
      glPushMatrix();
        glTranslated (-17.829, 5.0, -13.944);
        glRotated (-10, 1.0, 0.0, 0.0);
        glRotated (-10, 0.0, 0.0, 1.0);
        glCallList(tom16);
      glPopMatrix();
      glPushMatrix();
        glTranslated (17.0, 11.0, -3.0);
        glRotated (-65, 0.0, 1.0, 0.0);
        glCallList(boomCrash);
      glPopMatrix();
      glPushMatrix();
        glTranslated (12.3, 32.0, -12.3);
        glRotated (210, 0.0, 1.0, 0.0);
        if (c_inmotion)
        {
            float angle = 45.0 * (cdecay * (1.0 + Cos(cangle)));
            //DEBUG << "angle = " << angle;
            glRotated (angle, 1.0, 0.0, 0.0);
        }
        glRotated (15, 1.0, 0.0, 0.0);
        glPushMatrix();
          glScalef (10.0, 10.0, 10.0);
          //DrawAxes(12.0);
        glPopMatrix();
        glRotated(135, 0.0, 1.0, 0.0);
        glDisable(GL_CULL_FACE);
        glCallList(cymbalDispList);
        glEnable(GL_CULL_FACE);
      glPopMatrix();
      glPushMatrix();
        leftDrumstick.draw();
        //DrawAxes(12.0);
      glPopMatrix();
      glPushMatrix();
        rightDrumstick.draw();
        //DrawAxes(12.0);
      glPopMatrix();
      //glPushMatrix();
      //  glTranslated (14.0, 13.5, -13.0);
      //  glRotated (30, 0.0, 1.0, 0.0);
      //  glRotated (70, 1.0, 0.0, 0.0);
      //  glCallList(drumstickDispList);
      //glPopMatrix();
      glPushMatrix();
        glTranslated (0.0, -12.0, -23.5);
        glPushMatrix();
          glTranslated (-0.240, 6.51, 11.0);
          glRotated (mangle, 1.0, 0.0, 0.0);
          glCallList(mallet);
        glPopMatrix();
        glPushMatrix();
          glTranslated (0.0, 0.4, 1.7);
          glRotated (pangle, 1.0, 0.0, 0.0);
          glCallList(pedal);
        glPopMatrix();
        glCallList(kickPedal);
      glPopMatrix();
      glPushMatrix();
        glTranslated (19.5, -11.6, -24.5);
        glRotated (45, 0.0, 1.0, 0.0);
        glCallList(pedal);
      glPopMatrix();
      glPushMatrix();
        glTranslated (26.0, 8.0, -18.0);
        glRotated (-100, 0.0, 1.0, 0.0);
        glCallList(hihatStand);
      glPopMatrix();
      glPushMatrix();
        glTranslated (-26.0, 8.0, -6.0);
        glRotated (170, 0.0, 1.0, 0.0);
        glCallList(rideStand);
      glPopMatrix();

    // Cymbals do not have finite thickness, so need to turn off face culling
    // so we can see them from the back.
      glDisable(GL_CULL_FACE);
      glPushMatrix();
        glTranslated (26.0, 19.0, -18.0);
        glRotated (180, 0.0, 0.0, 1.0);
        glCallList(hihat14);
      glPopMatrix();
      glPushMatrix();
        glTranslated (26.0, 20.0, -18.0);
        glCallList(hihat14);
      glPopMatrix();
      glPushMatrix();
        glTranslated (-26.0, 24.25, -6.0);
        glRotated (-15, 0.0, 0.0, 1.0);
        glRotated (-15, 1.0, 0.0, 0.0);
        glRotated (-45, 0.0, 1.0, 0.0);
        if (r_inmotion)
        {
            float angle = -2.0 * (rdecay * (1.0 + Cos(rangle)));
            glRotated (angle, 1.0, 0.0, 0.0);
        }
        //DrawAxes(12.0);
        glCallList(ride20);
      glPopMatrix();
      glEnable(GL_CULL_FACE);

    /*
      glRotated (45, 0.0, 0.0, 1.0);
      glPushMatrix();
      glRotated (45, 0.0, 1.0, 0.0);
      glPushMatrix();
        //leftDrumstick.draw();
        //glColor3d (1.0, 1.0, 1.0);
        //DrawAxes(36.0);
        //glTranslated(leftDrumstick.getLocationX(), leftDrumstick.getLocationY(), leftDrumstick.getLocationZ());
        //glColor3d (1.0, 1.0, 0.0);
        //DrawAxes(24.0);
        //glRotated (leftDrumstick.getLocationRotZ(), 0.0, 0.0, 1.0);
        //glColor3d (1.0, 0.0, 1.0);
        //DrawAxes(12.0);
        //glRotated (leftDrumstick.getLocationRotY(), 0.0, 1.0, 0.0);
        PrintMatrix();
        glColor3d (0.0, 0.0, 1.0);
        DrawAxes(8.0);
        glRotated (90, 1.0, 0.0, 0.0);
        PrintMatrix();
        glColor3d (0.0, 1.0, 0.0);
        DrawAxes(4.0);
        glTranslated(0.0, 12.0, 0.0);
        PrintMatrix();
        glColor3d (1.0, 0.0, 0.0);
        DrawAxes(2.0);
        glCallList(drumstickDispList);
      glPopMatrix();
      glPopMatrix();
    */
}

void
DrumApp::ApplyPolygonMode()
{
    // Set Polygon mode
    if (polygonMode == 0)
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    else if (polygonMode == 1)
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    else if (polygonMode == 2)
        glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);

    // Enable anti-aliasing : only useful if we are doing a wireframe,
    // e.g., glPolygonMode(...,GL_LINE)
    if (polygonMode == 1)
    {
        glEnable(GL_LINE_SMOOTH);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
    }
    else
    {
        glDisable(GL_LINE_SMOOTH);
        glDisable(GL_BLEND);
    }
}

void
DrumApp::PlayInstrument (const char* instr, int policy)
{
    // Play the sound
    SoundManager::getInstance().Play(instr);

    // Do the animation for the drumstick
    if (policy == DRUMSTICK_POLICY_PREFER_RIGHT)
    {
        if (!rightDrumstick.isInMotion())
        {
            rightDrumstick.setLocation(instr);
            rightDrumstick.setInMotion();
        }
        else
        {
            leftDrumstick.setLocation(instr);
            leftDrumstick.setInMotion();
        }
    }
    else if (policy == DRUMSTICK_POLICY_PREFER_LEFT)
    {
        if (!leftDrumstick.isInMotion())
        {
            leftDrumstick.setLocation(instr);
            leftDrumstick.setInMotion();
        }
        else
        {
            rightDrumstick.setLocation(instr);
            rightDrumstick.setInMotion();
        }
    }
}

bool
DrumApp::LoadComposition (int num, const char *defaultComposition)
{
    char propName[512];
    const char *compFile;
    
    snprintf (propName, 512, "Composition.%d", num);
    ConfigManager& config = ConfigManager::getInstance();
    compFile = config.getProperty(propName, defaultComposition);

    if (!composition.loadFromFile (compFile))
    {
        WARN << "Unable to load Composition." << compositionNum 
             << " from file: " << compFile;
        return false;
    }

    loaded = 1;
    DEBUG << "Whole note duration = " << composition.getDurationWhole();
    return true;
}

bool
DrumApp::AdvanceComposition ()
{
    char propName[512];

    // We weren't able to load the initial file, so no sense in
    // trying to load other files...
    if (compositionNum == 0)
        return false;

    snprintf (propName, 512, "Composition.%d", compositionNum+1);
    ConfigManager& config = ConfigManager::getInstance();
    if (config.exists(propName))
        compositionNum++;
    else
        compositionNum = 1;

    return LoadComposition(compositionNum, NULL);
}

void
DrumApp::PrintMatrix()
{
    GLfloat mat[4][4];
    glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)mat);
    for (int i=0; i < 4; i++)
    {
        cout << "[";
        for (int j=0; j < 4; j++)
            cout << mat[j][i] << " ";
        cout << "]" << endl;
    }
}

// shadows
/*
 *  Set light
 *    light>0 bright
 *    light<0 dim
 *    light=0 off
 */
void
DrumApp::Light(int light)
{
    float Low[]  = {0.01*ambient/4,0.01*ambient/4,0.01*ambient/4,1.0};
    float Med[]  = {0.01*diffuse/4,0.01*diffuse/4,0.01*diffuse/4,1.0};
    GLfloat ambient_light[]  = {0.01*ambient, 0.01*ambient, 0.01*ambient, 1.00};
    GLfloat diffuse_light[]  = {0.01*diffuse, 0.01*diffuse, 0.01*diffuse, 1.00};
    GLfloat specular_light[] = {0.01*specular,0.01*specular,0.01*specular,0.00};
    GLfloat mAmbient[] = {0.2, 0.2, 0.2, 1.0};
    GLfloat mDiffuse[] = {0.8, 0.8, 0.8, 1.0};
    GLfloat mSpecular[] = {0.0, 0.0, 0.0, 1.0};
    GLfloat light0_position[] = {48.0, 48.0, 0.0, 1.0};
    Vector3D vector(light0_position[0], light0_position[1], light0_position[2],0,0,0);
    GLfloat direction[3];
    
    
    // direction of spotlight is always pointing at the origin
    // since that is where the drumset is drawn.
    direction[0] = vector.norm().i();
    direction[1] = vector.norm().j();
    direction[2] = vector.norm().k();

    // Set spotlight mode on/off according to global variable 
    GLint defaultCutoff = 180;
    if (spotlightOn)
        glLightiv(GL_LIGHT0, GL_SPOT_CUTOFF, &spotlightCutoff);
    else
        glLightiv(GL_LIGHT0, GL_SPOT_CUTOFF, &defaultCutoff);

    if (lightsOn && light)
    {
        glDisable(GL_LIGHTING);

        /* Make OpenGL use color from glColor() with lighting */
        //glColorMaterial (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
        //glEnable(GL_COLOR_MATERIAL);

        /* Normalize normal vectors */
        //glEnable(GL_NORMALIZE);
        glEnable(GL_RESCALE_NORMAL);

        /* Set light parameters for light 0 and enable lighting */
        glLightfv(GL_LIGHT0, GL_AMBIENT, light>0?ambient_light:Low);
        glLightfv(GL_LIGHT0, GL_DIFFUSE, light>0?diffuse_light:Med);
        glLightfv(GL_LIGHT0, GL_SPECULAR, light>0?specular_light:Low);

        glPushMatrix();
          glRotated (light0_angle, 0.0, 1.0, 0.0);
          glPushMatrix();
            glTranslatef (light0_position[0], light0_position[1], light0_position[2]);
            glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, mAmbient);
            glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, mDiffuse); 
            glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, mSpecular); 
            glColor3d(1.0, 1.0, 0.0); // yellow
            gluSphere (sphere, 1.0, 20, 20);
          glPopMatrix();
          glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
          glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, direction);
        glPopMatrix();

        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
    }
    else 
    {
        //glDisable(GL_COLOR_MATERIAL);
        glDisable(GL_NORMALIZE);
        glDisable(GL_LIGHTING);
        glDisable(GL_LIGHT0);
    }
}

/*
 *  Build Shadow Map
 */
void
DrumApp::ShadowMap(void)
{
#ifdef WITH_SHADOWS
    double Lpos[4];
    double Lmodel[16];  //  Light modelview matrix
    double Lproj[16];   //  Light projection matrix
    double Tproj[16];   //  Texture projection matrix
    double Dim=100.0;     //  Bounding radius of scene
    double Ldist;       //  Distance from light to scene center
    GLfloat light0_position[] = {48.0, 48.0, 0.0, 1.0};

    //  Save transforms and modes
    glPushMatrix();
    glPushAttrib(GL_TRANSFORM_BIT|GL_ENABLE_BIT|GL_VIEWPORT_BIT);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    //  No write to color buffer and no smoothing
    glShadeModel(GL_FLAT);
    glColorMask(0,0,0,0);
    // Overcome imprecision
    glEnable(GL_POLYGON_OFFSET_FILL);

    // Calculate light position based on light0_angle
    int th = light0_angle;
    Lpos[0] =  light0_position[0]*Cos(th)+light0_position[2]*Sin(th);
    Lpos[1] =  light0_position[1];
    Lpos[2] = -light0_position[0]*Sin(th)+light0_position[2]*Cos(th);
    Lpos[3] = 1;

    //INFO << "Lpos[0]="<<Lpos[0]<<" Lpos[1]="<<Lpos[1]<<" Lpos[2]="<<Lpos[2];

    //  Turn off lighting and set light position
    Light(0);

    //  Light distance
    Ldist = sqrt(Lpos[0]*Lpos[0] + Lpos[1]*Lpos[1] + Lpos[2]*Lpos[2]);
    if (Ldist<1.1*Dim) Ldist = 1.1*Dim;

    //  Set perspective view from light position
    glLoadIdentity();
    gluPerspective(114.6*atan(Dim/Ldist),1,Ldist-Dim,Ldist+Dim);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(Lpos[0],Lpos[1],Lpos[2] , 0,0,0 , 0,1,0);
    //  Size viewport to desired dimensions
    glViewport(0,0,Sdim,Tdim);

    // Redirect traffic to the frame buffer
    if (framebuf>0) glBindFramebuffer(GL_FRAMEBUFFER_EXT,framebuf);

    // Clear the depth buffer
    glClear(GL_DEPTH_BUFFER_BIT);
    // Draw all objects that can cast a shadow
    DrawScene(0);

    // Copy depth values into depth texture
    glActiveTextureA(GL_TEXTURE1);
    glCopyTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT,0,0,Sdim,Tdim,0);
    glActiveTextureA(GL_TEXTURE0);

    //  Retrieve light projection and modelview matrices
    glGetDoublev(GL_PROJECTION_MATRIX,Lproj);
    glGetDoublev(GL_MODELVIEW_MATRIX,Lmodel);

    // Set up texture matrix for shadow map projection,
    // which will be rolled into the eye linear
    // texture coordinate generation plane equations
    glLoadIdentity();
    glTranslated(0.5,0.5,0.5);
    glScaled(0.5,0.5,0.5);
    glMultMatrixd(Lproj);
    glMultMatrixd(Lmodel);

    // Retrieve result and transpose to get the s, t, r, and q rows for plane equations
    glGetDoublev(GL_MODELVIEW_MATRIX,Tproj);
    Svec[0] = Tproj[0];    Tvec[0] = Tproj[1];    Rvec[0] = Tproj[2];    Qvec[0] = Tproj[3];
    Svec[1] = Tproj[4];    Tvec[1] = Tproj[5];    Rvec[1] = Tproj[6];    Qvec[1] = Tproj[7];
    Svec[2] = Tproj[8];    Tvec[2] = Tproj[9];    Rvec[2] = Tproj[10];   Qvec[2] = Tproj[11];
    Svec[3] = Tproj[12];   Tvec[3] = Tproj[13];   Rvec[3] = Tproj[14];   Qvec[3] = Tproj[15];

    // Restore normal drawing state
    glShadeModel(GL_SMOOTH);
    glColorMask(1,1,1,1);
    glDisable(GL_POLYGON_OFFSET_FILL);
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    glPopAttrib();
    glPopMatrix();
    if (framebuf>0) glBindFramebuffer(GL_FRAMEBUFFER_EXT,0);

    //  Check if something went wrong
    ErrCheck("ShadowMap");
#endif
}


void
DrumApp::InitMap()
{
#ifdef WITH_SHADOWS
    unsigned int shadowtex; //  Shadow buffer texture id
    int n;

    // Load OpenGL extensions here
    char *extensions = (char *)glGetString(GL_EXTENSIONS);
    if (strstr(extensions,"EXT_framebuffer_object"))
    {
        glGenFramebuffers= 
            (glGenFramebuffersEXT_Func)SDL_GL_GetProcAddress("glGenFramebuffersEXT");
        assert(glGenFramebuffers != NULL);
        glBindFramebuffer= 
            (glBindFramebufferEXT_Func)SDL_GL_GetProcAddress("glBindFramebufferEXT");
        assert(glBindFramebuffer != NULL);
        glGenRenderbuffers= 
            (glGenRenderbuffersEXT_Func)SDL_GL_GetProcAddress("glGenRenderbuffersEXT");
        assert(glGenRenderbuffers != NULL);
        glBindRenderbuffer= 
            (glBindRenderbufferEXT_Func)SDL_GL_GetProcAddress("glBindRenderbufferEXT");
        assert(glBindRenderbuffer != NULL);
        glRenderbufferStorage= 
            (glRenderbufferStorageEXT_Func)SDL_GL_GetProcAddress("glRenderbufferStorageEXT");
        assert(glRenderbufferStorage != NULL);
        glFramebufferRenderbuffer= 
            (glFramebufferRenderbufferEXT_Func)SDL_GL_GetProcAddress("glFramebufferRenderbufferEXT");
        assert(glFramebufferRenderbuffer != NULL);
        glCheckFramebufferStatus= 
            (glCheckFramebufferStatusEXT_Func)SDL_GL_GetProcAddress("glCheckFramebufferStatusEXT");
        assert(glCheckFramebufferStatus != NULL);
        EXT_framebuffer_object = true;
    }
    else
    {
        WARN << "Missing EXT_framebuffer_object extension, forcing shadows off";
        shadowsOn = false;
        return;
    }

    if (strstr(extensions,"ARB_multitexture"))
    {
        glActiveTextureA =
            (glActiveTexture_Func)SDL_GL_GetProcAddress("glActiveTexture");
        assert(glActiveTextureA != NULL);
        glMultiTexCoord2fA =
            (glMultiTexCoord2f_Func)SDL_GL_GetProcAddress("glMultiTexCoord2f");
        assert(glMultiTexCoord2fA != NULL);
        ARB_multitexture = true;
    }
    else
    {
        WARN << "Missing ARB_multitexture extension, forcing shadows off";
        shadowsOn = false;
        return;
    }
    

    //  See if multi-textures are supported
    glGetIntegerv(GL_MAX_TEXTURE_UNITS,&n);
    DEBUG << "max texture units (multitexturing) = " << n;
    MultiTex = (n>1);

    if (!MultiTex)
    {
        WARN << "Only 1 texture units, shadows are not supported";
        shadowsOn = false;
        return;
    }

    //  Get maximum texture buffer size
    glGetIntegerv(GL_MAX_TEXTURE_SIZE,&shadowdim);
    DEBUG << "max texture size = " << shadowdim;
    //  Limit texture size to maximum buffer size
    glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT,&n);
    DEBUG << "max render buffer size = " << n;
    if (shadowdim > n) 
    {
        shadowdim = n;
    }

    //  Limit texture size to 2048 for performance
    if (shadowdim > 2048)
    {
        shadowdim = 2048;
    }
    DEBUG << "shadowdim=" << shadowdim;

    //  Do Shadow textures in MultiTexture
    //  XXX: Subsequent texture unit state calls will only affect
    //  this texture unit
    DEBUG << "Activating GL_TEXTURE1";
    glActiveTextureA(GL_TEXTURE1);

    //  Allocate and bind shadow texture
    glGenTextures(1,&shadowtex);
    glBindTexture(GL_TEXTURE_2D,shadowtex);

    //  Map single depth value to RGBA (this is called intensity)
    glTexParameteri(GL_TEXTURE_2D,GL_DEPTH_TEXTURE_MODE,GL_INTENSITY);

    //  Check if ambient shadows are supported
    glGetError();
    glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_FAIL_VALUE_ARB,0.5f);
    NoAmbient = (glGetError()==GL_INVALID_ENUM);

    if (NoAmbient)
    {
        DEBUG<< "Detected NoAmbient";
    }

    //  Texture modulate underlying objects
    glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);

    //  Set texture mapping to clamp and linear interpolation
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

    //  Set automatic texture generation mode to Eye Linear
    glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR);
    glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR);
    glTexGeni(GL_R,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR);
    glTexGeni(GL_Q,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR);

    // Switch back to default textures
    DEBUG << "Switching back to GL_TEXTURE0";
    glActiveTextureA(GL_TEXTURE0);

    // Only use framebuffer if it is big enough
    if (shadowdim > 512)
    {
        unsigned int renderbuf;
        int pow2;

        // Use power of two
        for (pow2=1;2*pow2<=shadowdim;pow2=pow2<<1);
        DEBUG << "pow2=" << pow2;
        Sdim = Tdim = shadowdim = pow2;
        DEBUG << "Sdim="<<Sdim<<", Tdim="<<Tdim;
        // Set up some renderbuffer state
        glGenFramebuffers(1,&framebuf);
        glBindFramebuffer(GL_FRAMEBUFFER_EXT,framebuf);
        //  Set render buffer size to shadow map
        glGenRenderbuffers(1,&renderbuf);
        glBindRenderbuffer(GL_RENDERBUFFER_EXT,renderbuf);
        glRenderbufferStorage(GL_RENDERBUFFER_EXT,GL_DEPTH_COMPONENT32,shadowdim,shadowdim);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT,GL_DEPTH_ATTACHMENT_EXT,GL_RENDERBUFFER_EXT,renderbuf);
        //  Don't write or read to visible color buffer
        glDrawBuffer(GL_NONE);
        glReadBuffer(GL_NONE);
        //  Make sure this all worked
        if (glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) 
        {
            ERROR << "Error setting up frame buffer";
        }
        glBindFramebuffer(GL_FRAMEBUFFER_EXT,0);

        //  Create shadow map
        ShadowMap();
    }
    else
    {
      //  Set shadow dim to maximum
      glGetIntegerv(GL_MAX_TEXTURE_SIZE,&shadowdim);
      ERROR << "Insufficient framebuffer";
    }

    //  Check if something went wrong
    ErrCheck("InitMap");
#endif
}


__END_NAMESPACE_DECL_
